package com.kunyi.web.service.file.core.handel.Impl;

import cn.hutool.core.util.StrUtil;
import com.kunyi.platform.redis.RedisService;
import com.kunyi.platform.tool.util.KyAssertUtil;
import com.kunyi.web.service.file.core.enums.KunyiRepositoryTypeEnum;
import com.kunyi.web.service.file.core.handel.FileLoginParams;
import com.kunyi.web.service.file.core.handel.KunyiRepository;
import com.kunyi.web.service.file.core.transform.FileTransform;
import com.kunyi.web.service.file.entity.domain.Upload;
import com.kunyi.web.service.file.service.IUploadService;
import com.kunyi.web.serviceapi.system.entity.FileDefaultInfoDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * @ClassName KunyiRepositoryAbstroller
 * @Description 文件上传抽象类
 * @Author zhenguo.zhuang
 * @Date 2024/5/28 11:10
 * @Version 1.0
 */
@Slf4j
public abstract class KunyiRepositoryAbstract implements KunyiRepository {
    /**
     * redis操作类
     */
    RedisService redisService;
    /**
     * 初始化目录
     */
    private String directory;
    /**
     *  业务处理操作类
     */
    IUploadService uploadService;
    /**
     * url
     */
    String  URL;
    /**
     *  业务类型
     */
    KunyiRepositoryTypeEnum type;
    /**
     *
     */
    String bucketName;

    /**
     * 文件路径分隔符
     */
    protected String osSeparator;

    /**
     * 文件存储根目录
     */
    protected String rootPath;



    /**
     * @author: zhenguo.zhuang
     * @Description 创建操作对象
     * @Date 10:33 2024/5/27
     * @Param loginParams: 具体的参数
     * @return boolean  true 成功 false 失败
     **/
    public boolean login(FileLoginParams loginParams) throws RuntimeException {
        try {
            this.uploadService = loginParams.getService();
            this.URL = loginParams.getUrl();
            FileDefaultInfoDto loginInfo = loginParams.getLoginInfo();
            String endPoint = loginInfo.getEndpoint();
            String accessKeyId = loginInfo.getAccessKey();
            String accessKeySecret = loginInfo.getAccessSecret();
            bucketName = loginInfo.getBucketName();
            directory = loginInfo.getDirectory();
            if (StrUtil.hasBlank(endPoint, accessKeyId, accessKeySecret)) {
                throw new RuntimeException("创建实例失败，因为参数不合法");
            }
            loginInSpecific(accessKeyId,accessKeySecret,endPoint);
            return true;
        } catch (Exception e) {
            log.error(e.toString(), e);
            throw new RuntimeException("登录失败", e);
        }
    }

    /**
     * @author: zhenguo.zhuang
     * @Description 读取文件
     * @Date 13:38 2024/5/28
     * @Param fileName: 文件名称，不是源文件名称，是生成后的名称
     * @return InputStream  流
     **/
    @Override
    public InputStream readStream(String fileName)  {
        try {
            if (StrUtil.hasBlank(fileName)) {
                throw new RuntimeException("请输入文件名");
            }
            String filePath;
            if (type().getCode().equals(KunyiRepositoryTypeEnum.LOCAL.getCode())){
                 filePath = FileTransform.getFilePath(fileName, rootPath, osSeparator);
            }else {
                 filePath = FileTransform.getFilePath(directory, fileName);
            }
            KyAssertUtil.isTrue(isFileExistAgo(filePath), "读取的文件已被删除");
            return readStreamsSpecific(filePath);
        } catch (Exception e) {
            log.error(e.toString(), e);
            throw  new RuntimeException("读取的文件已被删除", e);
        }
    }
    /**
     * @author: zhenguo.zhuang
     * @Description 上传文件根据MultipartFile
     * @Date 10:44 2024/5/29
     * @Param file: 文件
     * @return String  访问地址
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String uploadMultipartFile(MultipartFile file) {
        try {
            //第一步：生成文件目录
            String filePath;
            if (type().getCode().equals(KunyiRepositoryTypeEnum.LOCAL.getCode())){
                filePath = FileTransform.generateFilePath(rootPath, osSeparator);
            }else {
                filePath = FileTransform.generateFilePath(directory);
            }
            //第二步: 判断文件是否存在
            boolean fileExist = isFileExistAgo(filePath);
            if (!fileExist) { //不存在，则创建目录
                KyAssertUtil.isTrue(createDirAgo(filePath), "目录不存在，在创建目录时失败");
            }
            //第三步：生成文件路径
            String originalFilename = file.getOriginalFilename();
            String fileName = FileTransform.generateFileName(originalFilename);
            String appendFilePath = filePath + fileName;
            //第四步:上传

            InputStream inputStream = file.getInputStream();
            uploadSpecific(filePath,fileName,appendFilePath,inputStream,file.getSize(),file,null);
            //上传文件时的key为上传目录加上传的文件名
            Upload upload = new Upload().setName(fileName).setSourceName(originalFilename).setRepositoryType(type().getCode()).setFileSize(file.getSize()).setMimeType(file.getContentType());
            uploadService.save(upload);
            inputStream.close();
            //文件访问的路径
            return FileTransform.generateFileUrl(fileName, URL);
        } catch (IOException e) {
            log.error(e.toString(), e);
            throw new RuntimeException("上传文件失败", e);
        }
    }
    /**
     * @author: zhenguo.zhuang
     * @Description 上传文件根据File
     * @Date 10:44 2024/5/29
     * @Param file: 文件
     * @return String  访问地址
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String upload(File file) {
        try {
            //第一步：生成文件目录
            String filePath;
            if (type().getCode().equals(KunyiRepositoryTypeEnum.LOCAL.getCode())){
                 filePath = FileTransform.generateFilePath(rootPath, osSeparator);
            }else {
                 filePath = FileTransform.generateFilePath(directory);
            }
            //第二步: 判断文件是否存在
            boolean fileExist = isFileExistAgo(filePath);
            if (!fileExist) { //不存在，则创建目录
                KyAssertUtil.isTrue(createDirAgo(filePath), "目录不存在，在创建目录时失败");
            }
            //第三步：生成文件路径
            String originalFilename = file.getName();
            String fileName = FileTransform.generateFileName(originalFilename);
            String appendFilePath = filePath + fileName;
            FileInputStream inputStream = new FileInputStream(file);
            //第四步:上传
            uploadSpecific(filePath,fileName,appendFilePath,inputStream,file.length(),null,file);
            Upload upload = new Upload().setName(fileName).setSourceName(originalFilename).setRepositoryType(type().getCode()).setFileSize(file.length());
            uploadService.save(upload);
            inputStream.close();
            //文件访问的路径
            return FileTransform.generateFileUrl(fileName,URL);
        } catch (IOException e) {
            log.error("文件上传失败", e);
            throw new RuntimeException("OSS文件上传失败",e);
        }
    }

    /**
     * @author: zhenguo.zhuang
     * @Description 删除文件
     * @Date 10:43 2024/5/29
     * @Param fileName: 文件名称
     * @return boolean  true 成功 false 失败
     **/
    @Override
    public boolean deleteFile(String fileName){
        try {
            String filePath;
            if (type().getCode().equals(KunyiRepositoryTypeEnum.LOCAL.getCode())){
                 filePath = FileTransform.getFilePath(fileName, rootPath, osSeparator);
            }else {
                filePath= FileTransform.getFilePath(directory,fileName);
            }
            KyAssertUtil.isTrue(isFileExistAgo(filePath),"删除文件不存在");
            return deleteFileSpecific(filePath);
        }catch (Exception e){
            log.error("删除文件失败",e);
            throw new RuntimeException("OSS删除文件失败",e);
        }
    }

    /**
     * @author: zhenguo.zhuang
     * @Description 校验文件是否存在,前置操作
     * @Date 14:49 2024/5/28
     * @Param filePath:
     * @return boolean
     **/
    boolean isFileExistAgo(String filePath) {
        if (StrUtil.hasBlank(filePath)) {
            return false;
        }
        if (!type().getCode().equals(KunyiRepositoryTypeEnum.LOCAL.getCode())){
            filePath=removeFirstStr(filePath);
        }
        try {
            return isFileExistSpecific(filePath);
        }catch (Exception e){
            throw new RuntimeException("文件不存在", e);
        }
    }

    /**
     * @author: zhenguo.zhuang
     * @Description 创建文件前置操作
     * @Date 14:50 2024/5/28
     * @Param filePath: 文件路劲
     * @return boolean  true 成功  false失败
     **/
    boolean createDirAgo(String filePath){
        if (StrUtil.hasBlank(filePath)) {
            return false;
        }
        String path = filePath;
        if (!type().getCode().equals(KunyiRepositoryTypeEnum.LOCAL.getCode())) {
            filePath = removeFirstStr(filePath);
            //要创建的文件夹名称,在满足Object命名规则的情况下以"/"结尾
            if (!filePath.endsWith("/")) {
                path = filePath + "/";
            }
        }
        try {
            return createDirSpecific(path);
        } catch (Exception e) {
            log.error(e.toString(), e);
            throw new RuntimeException("创建目录失败", e);
        }
    }

    /**
     * @author: zhenguo.zhuang
     * @Description  读取文件抽象接口
     * @Date 11:32 2024/5/28
     * @Param filePath: 文件路劲
     * @return InputStream
     **/
    abstract InputStream readStreamsSpecific(String filePath) throws Exception;


    /**
     * @author: zhenguo.zhuang
     * @Description
     * @Date 11:32 2024/5/28
     * @Param accessKeyId:  密钥id
     * @Param accessKeySecret:密钥对
     * @return endPoint:endPoint
     **/
    abstract void loginInSpecific(String accessKeyId,String accessKeySecret,String endPoint) throws Exception;
    /**
     * @author: zhenguo.zhuang
     * @Description 校验文件存不存在抽象接口
     * @Date 14:08 2024/5/28
     * @Param filePath: 文件路劲
     * @return boolean true 存在 false 不存在
     **/
    abstract boolean isFileExistSpecific(String filePath) throws RuntimeException;
    /**
     * @author: zhenguo.zhuang
     * @Description 创建目录抽象接口
     * @Date 14:10 2024/5/28
     * @Param filePath: 创建的目录
     * @return boolean true 成功 false 失败
     **/
     abstract boolean createDirSpecific(String filePath) throws RuntimeException;

    /**
     * @author: zhenguo.zhuang
     * @Description 上传文件操作抽象接口
     * @Date 10:36 2024/5/29
     * @Param filePath: 生成的文件路径（想报错到哪）
	 * @Param fileName: 生成的文件名称（）
	 * @Param appendFilePath: 生成的文件路径+生成的文件名称
	 * @Param inputStream: 流  （可选）
	 * @Param length: 文件大小
	 * @Param multipartFile: 具体的文件
	 * @Param file: 具体的文件
     * @return void   不报错则正常
     **/
    abstract void  uploadSpecific(String filePath,String fileName,String appendFilePath, InputStream inputStream , Long length,MultipartFile multipartFile,File file) throws RuntimeException, IOException;

    /**
     * @author: zhenguo.zhuang
     * @Description 删除文件抽象接口
     * @Date 10:39 2024/5/29
     * @Param filePath: 文件路劲
     * @return boolean  true 删除成功  false 删除失败
     **/
    abstract boolean deleteFileSpecific(String filePath) throws RuntimeException;


    /**
     * @author: zhenguo.zhuang
     * @Description 存储类型抽象接口
     * @Date 14:33 2024/5/28
     * @Param :
     * @return KunyiRepositoryTypeEnum
     **/
    abstract KunyiRepositoryTypeEnum type();

    /**
     * @author: zhenguo.zhuang
     * @Description 去除文件之前的“/”
     * @Date 15:22 2024/3/13
     * @Param path: 路径
     * @return String
     **/
    String removeFirstStr(String path) throws RuntimeException{
        if(path.startsWith("/")){
            path = path.substring(1,path.length());
        }
        if(path.startsWith("/")){
            removeFirstStr(path);
        }
        return path;
    }


}
